'''
曲线拟合工具
作者：侯展意
协议：木兰2.0
'''
import tkinter as tk
from tkinter import ttk
import traceback
import numpy as np
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
from matplotlib.backend_bases import key_press_handler
from matplotlib.figure import Figure
import matplotlib.pyplot as plt
import time
import threading
import os
import sys
import multiprocessing
import algorithm
from algorithm import loadVariables
plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号

def splitStrToDataList(st=''):
    if st.find(',')!=-1:
        return st.strip().split(',')
    else:
        return st.strip().split(' ')

def genData(textX='1,2,3',textY='3,4,5'):
    varDic=loadVariables()
    print(textX,textY)
    if textX in varDic.keys():
        xArray=np.array(varDic[textX])
    else:
        lX=splitStrToDataList(textX)
        xArray=np.zeros(len(lX))
        for i in range(len(lX)):
            xArray[i]=float(lX[i])
    if textY in varDic.keys():
        yArray=np.array(varDic[textY])
    else:
        lY=splitStrToDataList(textY)
        yArray=np.zeros(len(lY))
        for i in range(len(lY)):
            yArray[i]=float(lY[i])
    return xArray,yArray

class controlFrame(ttk.Frame):
    def __init__(self,master,**kwargs):
        super().__init__(master,**kwargs)
        self.refreshingStatus=True
        self.refreshingStatusButton = ttk.Button(master=self, 
             text="计算",command=self.stopOrContinueRefreshing)#控制是否停止的按钮
        
        
        funcArgsInputLabel=ttk.Label(master=self,text='输入函数变量名')
        self.funcArgsInputEntry=ttk.Entry(master=self)
        
        
        self.xEntryLabel=ttk.Label(master=self,text='输入x')
        self.xEntry=ttk.Entry(master=self)
        self.yEntryLabel=ttk.Label(master=self,text='输入y')
        self.yEntry=ttk.Entry(master=self)


        
        funcInputLabel=ttk.Label(master=self,text='输入函数表达式' )
        
        self.funcInputEntry=tk.Text(master=self,height=4)
        

        refreshingIntervalInputLabel=ttk.Label(master=self,text='设定刷新间隔(ms):')

        self.renderingTimeLabel=ttk.Label(master=self,text='渲染时间消耗：1000 ms')
        
        self.refreshingRateEntry=ttk.Entry(master=self)
        self.refreshingRateEntry.insert(0,'2000')

        
        self.xEntryLabel.pack(expand=1,fill=tk.X)
        self.xEntry.pack(expand=1,fill=tk.X)
        
        self.yEntryLabel.pack(expand=1,fill=tk.X)
        self.yEntry.pack(expand=1,fill=tk.X)
        
        funcArgsInputLabel.pack(expand=1,fill=tk.X)
        self.funcArgsInputEntry.pack(expand=1,fill=tk.X)
        
        funcInputLabel.pack(expand=1,fill=tk.X)
        self.funcInputEntry.pack(expand=1,fill=tk.X)
        
        self.refreshingStatusButton.pack()
        

        self.xEntry.insert(0,'1,2,3,4,5')
        self.yEntry.insert(0,'1.1,2.1,3.3,5.0,4.9')
        self.funcArgsInputEntry.insert(0,'a,b,c,d')
        self.funcInputEntry.insert(tk.END,'a * x ** 3 + b * x ** 2 + c * x + d')
        
    def getArgsAndFunc(self):
        argsStr=self.funcArgsInputEntry.get().strip()
        funcStr=self.funcInputEntry.get(0.0,'end').strip()
        return argsStr,funcStr
        
    def stopOrContinueRefreshing(self):
        self.master.refresh()

    def getXY(self):
        xStr=self.xEntry.get().strip()
        yStr=self.yEntry.get().strip()
        return genData(xStr,yStr)
        

class matplotlibFloatWindow(tk.Tk):
    def __init__(self):
        super().__init__()
        self.title("曲线拟合工具")
        self.wm_attributes('-topmost',1)
        
        self.figure=Figure(figsize=(5,4),dpi=100)
        self.canvas=FigureCanvasTkAgg(self.figure, master=self)
        self.font=('Arial',10)
        
        self.canvas.get_tk_widget().pack(side=tk.TOP,  # 上对齐
                            fill=tk.BOTH,  # 填充方式
                            expand=tk.YES)  # 随窗口大小调整而调整

        toolbar = NavigationToolbar2Tk(self.canvas, self)
        toolbar.update()
        self.canvas._tkcanvas.pack(side=tk.LEFT,  # get_tk_widget()得到的就是_tkcanvas
                      fill=tk.BOTH,expand=tk.YES)
                # 创建一个按钮,并把上面那个函数绑定过来
                
        self.statLabel=tk.Label(master=self,text='',anchor=tk.W,height=10)
        self.statLabel.pack(expand=1,fill=tk.BOTH)

        self.controlPanel=controlFrame(master=self)
        self.controlPanel.pack(side=tk.LEFT)
        
        self.refresh()
        
    def reportStatus(self,text,warning=False):#报错函数。
        if(warning==True):
            self.statLabel.config( fg='red',bg='white')
        else:
            self.statLabel.config( fg='black',bg='white')

        self.statLabel.config(text=text)

    def showFitResult(self,popt,pcov,argsStr):
        st=''
        stdevErr=np.sqrt(np.diag(pcov))
        print('stdevErr',stdevErr)
        
        argsList=argsStr.split(',')
        for i in range(len(popt)):
            st+='%s = %f, cov:%f\n'%(argsList[i],popt[i],stdevErr[i])
        print(pcov)
##        st+='resid:%f\n'%pcov
        self.reportStatus(st)
    
    def refresh(self):
        self.figure.clf()
        ax=self.figure.add_subplot(111)
        try:
            x,y=self.controlPanel.getXY()
            print("hhh")
            print(x)
            print(y)
            argsStr,funcStr=self.controlPanel.getArgsAndFunc()
            popt,pcov,yvals=algorithm.fit(x,y,argsStr,funcStr)
            self.showFitResult(popt,pcov,argsStr)
            plot1 = ax.plot(x, y, 's', label='original values')
            argsor=np.argsort(x,axis=0)
            plot2 = ax.plot(x[argsor], yvals[argsor], 'r', label='fit values')
            ax.set_xlabel('x')
            ax.set_ylabel('y')
            ax.legend(loc=4)  # 指定legend的位置右下角
            ax.set_title('curve_fit')
            ax.grid()
            self.canvas.draw()
            self.canvas.flush_events()
        except Exception as e:
            exc_type, exc_value, exc_traceback = sys.exc_info()
            e=traceback.format_exception(exc_type, exc_value,
                                          exc_traceback)
            traceback.print_exc()
            st=''
            for s in e:
                st+=s      
            self.reportStatus(st,warning=True)



    def on_key_event(self,event):
        """键盘事件处理"""
        print("你按了%s" % event.key)
        key_press_handler(event,self.canvas)


def _quit():
    """点击退出按钮时调用这个函数"""
    root.quit()  # 结束主循环
    root.destroy()  # 销毁窗口

def run():
    root=matplotlibFloatWindow()
    root.canvas.mpl_connect('key_press_event', root.on_key_event)
    root.mainloop()
if __name__=='__main__':
    print(genData())
